iT邦幫忙

2022 iThome 鐵人賽

DAY 10
0
Modern Web

前端蛇行撞牆記系列 第 10

Day10 前端蛇行撞牆記 - array-like 可以用陣列方法嗎?

  • 分享至 

  • xImage
  •  

前言

當我在嘗試用類陣列使用 push()的時候,發現居然可以欸?!

好,我明白可能是因爲類陣列偽裝得太像甚至騙過了陣列方法,所以才可以用,所以我去查詢ECMA對陣列方法的規範,

於是我看到這段話:

The push function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.

我還以為陣列方法的標準之嚴格只能被陣列使用的,但陣列方法居然挑明地說就是故意讓大家都可以使用拉~~
WHAT?! OK, let's go into it ~~

不過先來介紹一下Array.prototype的用法

array

不知道你有沒有想過,為什麼在使用陣列的時候可以這樣直接在array.push()直接這樣做:

a.push(4)
console.log(a)

// [1,2,3,4]

可是查詢MDN的時候,全名應該是:Array.prototype.push(),跟我原本用的方式很不一樣耶,這樣要怎麼用?

Array.prototype.push()

其實是可以用 Array.prototype.push() 這樣的方式去寫!
只是用這個方式的話,我要把要執行的陣列放在哪裡呢?

const a = [1,2,3]
Array.prototype.push.call(a, 4)

console.log(a)
// [1,2,3,4]
  • call()來綁定this,就是要用call()來綁定現在要執行的陣列
  • 原本push 後面的小括號不用了,給 call()來綁定現在的this值,而原本要push的再放到第二個參數,這種寫法也可以有一樣的效果。

原本在學call()的時候都是用在function身上,原來他也可以用在陣列。
MDN - The call() allows for a function/method belonging to one object to be assigned and called for a different object.

好,這種pop(), push(), shift(), unshift() 這種比較容易,只是傳一個值或者不傳,來試試看參數要帶入有callback function的方法。

Array.prototype.map()

const ac = Array(1, 2, 3);
const bc = Array.prototype.map.call(ac, (item) => item + 1);

console.log(bc);
// [ 2, 3, 4 ]

其實跟正常使用map的方式一樣,只是把 function放在this的後面而已,call()就會來用array.prototype.map()的方式去執行了。

那...object也可以嗎?
終於要進入正題了

array-like object

前言有提到,查詢ECMA的時候幾乎都有一個說法:

The push function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.

自己翻譯:
push函式是有意通用的,他並不會要求一定要是一個陣列。所以他可以被轉變成其他的物件來當作方法使用。

這是在鼓勵我去把method用在不是array的物件上嗎?

Array.prototype.push()

  • 如果有看過上一篇文應該知道,我先來用最經典的類陣列試個水溫,
    然後我沒有手動加length
var arrayLike = {
  0: 2,
  1: 4,
  2: 6,
};
Array.prototype.push.call(arrayLike, 8);
console.log(arrayLike);
// { 
// '0': 8,
// '1': 4,
// '2': 6,
// length: 1 
// }

可以是可以啊,可是變得超怪的啊!

居然原本的 key:0 的 value 被替換成新 push 進去的 8 ?
然後自動產生length: 1,可是明明就有3個property!
看起來是他只認後來使用方法進去的值。

  • 不相信的我,繼續push好幾個

push進去的值不斷地取代一開始我自訂的值,終於取代完之後終於開始從後面push值進去,而length在全部都取代完之後也正常了..

  • 可是如果我一開始就給length!!
var arrayLike = {
  0: 2,
  1: 4,
  2: 6,
  length: 3,
};
Array.prototype.push.call(arrayLike, 8);
console.log(arrayLike);
// { '0': 2, '1': 4, '2': 6, '3': 8, length: 4 }

一切都正常了,namaste

來試試看很困難需要遍歷的map(), reduce()

Array.prototype.map()

const newArrayLike = Array.prototype.map.call(arrayLike, (item) => item + 1);

console.log(newArrayLike);
// [ 3, 5, 7, 9 ]

他回來了!可是自動變成陣列了

Array.prototype.reduce()

const newArrayLike = Array.prototype.reduce.call(
  arrayLike,
  function (current, previous) {
    current += previous;
    return current;
  },
  0
);

console.log(newArrayLike);
// 12

居然...也是可以?!

可是在迭代那篇文章中,才說到由於陣列跟物件儲存的方式不一樣,陣列是可以被遍歷的,但物件是沒辦法的,所以他沒有iterable是很正常的,這邊我都能夠理解。

可是現在這個array-like object可以被map()reduce()遍歷耶!!

我想應該是因為有 length 的關係,只要有length JS就可以盡力把它做到陣列可以做到的功能。畢竟JS就是個很沒節操的語言啊~

好了,大家腦袋是不是更混亂了呢?
為什麼我一直在打臉自己的文章啊!!

但是反正,就是不要把object做array做的事!
不然至少把他用Array.from()把他變成一個陣列再做吧!

(但是我一直做,瘋掉)

明天見拉!


參考資料:What does Array.prototype.join.call do in the background for a string?
Array.prototype.concat is not generic
MDN
ECMA


上一篇
Day9 前端蛇行撞牆記 - 淺入了解array-like
下一篇
Day11 前端蛇行撞牆記 - 屬性描述器
系列文
前端蛇行撞牆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言